//TODO:original version had ignore-error feature
#include "croot.h"

#include <X11/Xatom.h>
#include <X11/Xregion.h>
#include <X11/extensions/Xcomposite.h>

#include <buola/gui/x11/cvisual.h>

#include <buola/gui/x11/ccomposite.h>
#include <buola/image/generator/gaussian.h>
#include <buola/image/cgc.h>
#include <buola/gui/x11/cpixmap.h>
#include <buola/gui/x11/cx11display.h>
#include <buola/gui/x11/cx11toolkit.h>

#include <buola/gl/ctexture.h>

#include "ceventmanager.h"
#include "cclient.h"
#include "cmanagedclient.h"
#include "coverrideclient.h"
#include "cdecoration.h"
#include "scenerenderer/ccuberenderer.h"
#include "desktoprenderer/cshufflerenderer.h"
#include "desktoprenderer/cdefaultdesktoprenderer.h"

#include <buola/algorithm/container.h>
#include <buola/app/ctimer.h>

namespace buola { namespace bwm {

CRoot *gRoot;

///////////////////////////////////////////////////////////////////
//                                                               //
//                   creation and initialization                 //
//                                                               //
///////////////////////////////////////////////////////////////////

CRoot::CRoot()
{
    gRoot=this;
    mStarted=false;
    mActiveClient=nullptr;
    mSupportingWMCheckWindow=nullptr;

    mTimer=nullptr;
    mFlags=COMP_NONE;
    mDesktopRenderer=nullptr;
    mSceneRenderer=nullptr;

    mCubeRenderer=nullptr;
    mShuffleRenderer=nullptr;
    mDefaultDesktopRenderer=nullptr;

    mClientData.mNumSlots=0;

    mGoingOut=false;
    mShowStatistics=true;
    mUseDepthTest=false;

    mX11Window=gui::x11::get_x11_toolkit()->GetRootWindow();
    mX11Window->SelectInput(gui::EVENT_GEOMETRY|gui::EVENT_FOCUS|gui::EVENT_CHILDREQUESTS|gui::EVENT_CHILDGEOMETRY|
                            gui::EVENT_COLORMAP|gui::EVENT_PROPERTY|gui::EVENT_KEYDOWN|gui::EVENT_KEYUP|gui::EVENT_EXPOSE);
    mID=mX11Window->GetX11ID();
    
    mTime.mCurrent=CMonotonicClock::now();
    mTime.mPrevious=mTime.mCurrent-chrono::seconds(1);
    mTime.mElapsed=chrono::seconds(1);

    mEventManager=new CEventManager;
    mEventManager->Start();

    gui::x11::CX11SyncCounter::GetSystemCounterList();

    XGrabServer (gui::x11::get_default_x11_display()->GetDisplay());

    mScreenNumber=gui::x11::get_default_x11_display()->GetScreenNumber();

    mRedrawTime=chrono::milliseconds(12);
    mOptimalRedrawTime=mRedrawTime;

    mTimer=new CTimer(mRedrawTime);
    mTimer->Enable();
    mTimer->sTimer.Connect(&CRoot::OnComposeTimer,this);

    InitComposite();
    InitScreen();       //Only one screen at the moment (why more?)
    InitWindows();

    XUngrabServer (gui::x11::get_default_x11_display()->GetDisplay());

    mStarted=true;

    WMRestack();
    WMCirculate(false);

    DamageAll();
    msg_info() << "bbb\n";
}

CRoot::~CRoot()
{
    //TODO:abandon windows in a nice way
    XSetInputFocus(gui::x11::get_default_x11_display()->GetDisplay(), PointerRoot,
                RevertToPointerRoot,CurrentTime);
}

void CRoot::InitScreen()
{
    mDesktops.mCount=0;
    mDesktops.mActive=0;
    CreateDesktop(L"Tonto");
    CreateDesktop(L"Buuu");
    CreateDesktop(L"aaa");
    CreateDesktop(L"Tonto");

    mSupportingWMCheckWindow=throwing_cast<gui::x11::CX11Window*>(gui::x11::get_x11_toolkit()->GetNewProxyWindow());

    gui::x11::icccm::write_net_supporting_wm_check(mSupportingWMCheckWindow->GetX11ID());
    gui::x11::icccm::write_net_supported();
    NETSetDesktopInfo();
    NETSetDesktopNames();
    NETSetCurrentDesktop();

    BuolaSetWMExtensions();

    ///\todo
    //SetPointer(gui::CPointer::STOCK_DEFAULT);

    ///\todo
//     mAltTab.Set(KEY_TAB,KEYMOD_ALT,gui::CAccelerator::GLOBAL,this);
//     mAltShiftTab.Set(KEY_TAB,KEYMOD_ALT|KEYMOD_SHIFT,gui::CAccelerator::GLOBAL,this);
//     mAltF4.Set(KEY_F4,KEYMOD_ALT,gui::CAccelerator::GLOBAL,this);
//     mCtrlAltRight.Set(KEY_RIGHT,KEYMOD_ALT|KEYMOD_CONTROL,gui::CAccelerator::GLOBAL,this);
//     mCtrlAltLeft.Set(KEY_LEFT,KEYMOD_ALT|KEYMOD_CONTROL,gui::CAccelerator::GLOBAL,this);
//     mWinS.Set(KEY_S,KEYMOD_WIN,gui::CAccelerator::GLOBAL,this);
//     mWinD.Set(KEY_D,KEYMOD_WIN,gui::CAccelerator::GLOBAL,this);
    
    gui::get_toolkit()->Synchronize();
}

void CRoot::InitWindows()
{
    //scan for initial windows
    unsigned int i, lCount;
    Window l1, l2, *lWins;
    XWindowAttributes lAttr;

    XQueryTree(gui::x11::get_default_x11_display()->GetDisplay(),GetID(), &l1, &l2, &lWins,
                                                        &lCount);

    for(i=0;i<lCount;i++)
    {
        XGetWindowAttributes(gui::x11::get_default_x11_display()->GetDisplay(),lWins[i],&lAttr);

        if(lAttr.map_state!=IsViewable) //When it is mapped...
            continue;

        CreateClientFor(lWins[i],true);
    }
    XFree((void *)lWins);
}

///////////////////////////////////////////////////////////////////
//                                                               //
//                        client management                      //
//                                                               //
///////////////////////////////////////////////////////////////////

CClient *CRoot::GetClient(Window pID)
{
    for(int i=0;i<mClients.size();i++)
    {
        if(mClients[i]->mID==pID)
            return mClients[i];
    }

    return nullptr;
}

CManagedClient *CRoot::GetManagedClient(Window pID)
{
    for(int i=0;i<mManagedClients.size();i++)
    {
        if(mManagedClients[i]->mID==pID)
            return mManagedClients[i];
    }

    return nullptr;
}

CDecoration *CRoot::GetDecoration(Window pID)
{
    for(int i=0;i<mClients.size();i++)
    {
        CDecoration *lD=mClients[i]->mDecoration;
        if(!lD) continue;
        for(int j=0;j<lD->mDecoWindows.size();j++)
        {
            if(lD->mDecoWindows[j]==pID)
                return lD;
        }
    }

    return nullptr;
}

CClient *CRoot::CreateClientFor(Window pID,bool pWMStarting)
{
    if(IsSpecialWindow(pID)) return nullptr;

    XWindowAttributes lXAttrs;
    //!check if the window still exists, and get attributes
    if(!XGetWindowAttributes(gui::x11::get_default_x11_display()->GetDisplay(),pID,&lXAttrs))
    {
        msg_warn() << "window doesn't exist anymore\n";
        return nullptr;
    }

    if(lXAttrs.override_redirect&&lXAttrs.c_class==InputOnly)
        return nullptr;
    
    CClient *lClient;
    
    if(lXAttrs.override_redirect)
        lClient=new COverrideClient(pID,lXAttrs);
    else
        lClient=new CManagedClient(pID,lXAttrs);

    if(pWMStarting)
    {
        if(lXAttrs.map_state!=IsUnmapped)
        {
            lClient->OnMapNotify(true);
        }
    }
    
    mClients.push_back(lClient);

    NETSetClientList();

    return lClient;
}

///////////////////////////////////////////////////////////////////
//                                                               //
//                        window management                      //
//                                                               //
///////////////////////////////////////////////////////////////////

void CRoot::CreateDesktop(const std::wstring& pName,const CRect_i& pWorkArea,
                                const CPoint_i& pViewport)
{
    int i=mDesktops.mCount;
    mDesktops.mName[i]=pName;
    if(pWorkArea.l>=0)
    {
        mDesktops.mWorkArea[i]=pWorkArea;
    }
    else
    {
        mDesktops.mWorkArea[i]=CRect_i(CPoint_i(0,0),CSize_i(GetSize()));
    }
    mDesktops.mViewport[i]=pViewport;
    mDesktops.mVirtualViewport[i]=CPoint_i(GetSize().x*i,0);
    mDesktops.mVisible[i]=true;

    mDesktops.mCount++;

    OnDesktopsChanged();
}

void CRoot::SwitchToDesktop(int pDesktop,bool pAnimate)
{
    if(pDesktop==mDesktops.mActive) return;

    if(pDesktop<0)
        pDesktop+=mDesktops.mCount;

    if(pDesktop<0||pDesktop>=mDesktops.mCount)
    {
        msg_error() << "Trying to switch to desktop " << pDesktop << "\n";
        return;
    }

    if(pAnimate)
    {
        mSceneRenderer->AnimateDesktopSwitch(mDesktops.mActive,pDesktop);
    }

    mDesktops.mActive=pDesktop;
    NETSetCurrentDesktop();

    for(int i=0;i<mClientsStack.size();i++)
    {
        mClients[i]->OnDesktopSwitched();
    }

    if(mActiveClient&&!mActiveClient->IsOnDesktop(mDesktops.mActive))
    {
        WMCirculate(false);
    }
}

void CRoot::WMCirculate(bool pIconic)
{
    if(!mManagedClients.size()) return;

    CManagedClient *lClient=nullptr;

    int i=0;

    if(mActiveClient)   //Find starting point
    {
        for(i=0;i<mManagedClients.size();i++)
        {
            if(mManagedClients[i]==mActiveClient)
            {
                if(mManagedClients[i]->CanBeActivated(pIconic))
                    lClient=mManagedClients[i];

                break;
            }
        }
    }

    for(int j=((i<mManagedClients.size()-1)?i+1:0);j!=i;
            j=((j<mManagedClients.size()-1)?j+1:0))
    {
        if(mManagedClients[j]->CanBeActivated(pIconic))
        {
            lClient=mManagedClients[j];
            if(lClient->GetWindowType()!=gui::EWindowType::DESKTOP&&
                lClient->GetWindowType()!=gui::EWindowType::DOCK)
                break;
        }
    }

    WMActivate(lClient);
}

void CRoot::WMRevert()
{
    while(!mRevertStack.empty())
    {
        CManagedClient *lClient=mRevertStack.back();
        mRevertStack.pop_back();
        
        //We must check that the proxy still exists
        for(int i=0;i<mManagedClients.size();i++)
        {
            if(mManagedClients[i]!=lClient) continue;
            if(lClient->CanBeActivated(false))
            {
                WMActivate(lClient);
                return;
            }
        }
    }

    WMCirculate(false);
}

void CRoot::WMActivate(CManagedClient *pNew,bool pRaise)
{
    if(pNew!=mActiveClient)
    {
        if(pNew&&mActiveClient)
            mRevertStack.push_back(mActiveClient);

        CManagedClient *lOld=mActiveClient;
        mActiveClient=nullptr;
        if(lOld)
            lOld->DeActivate();
        mActiveClient=pNew;

        if(pNew)
        {
            pNew->Activate();
            if(pRaise)
                WMRaise(pNew);

            //Telling the world the active window
            gui::x11::get_default_x11_display()->SetWindowProperty(GetID(),gui::x11::get_default_x11_display()->Atoms().mNetActiveWindow,XA_WINDOW,
                        32,1,(unsigned char*)&pNew->mID);

            if(!pNew->IsOnDesktop(mDesktops.mActive))
            {
                for(int i=0;i<mDesktops.mCount;i++)
                {
                    if(pNew->IsOnDesktop(i))
                    {
                        SwitchToDesktop(i,true);
                        break;
                    }
                }
            }
        }
        else
        {
//          SetFocus();
        }
    }
}

void CRoot::WMIncStrut(int pL,int pT,int pR,int pB)
{
    if(!pL&&!pT&&!pR&&!pB) return;

    for(int i=0;i<mDesktops.mCount;i++)
    {
        mDesktops.mWorkArea[i].l+=pL;
        mDesktops.mWorkArea[i].t+=pT;
        mDesktops.mWorkArea[i].r-=pR;
        mDesktops.mWorkArea[i].b-=pB;
    }

    NETSetDesktopInfo();
}

void CRoot::WMRestack()
{

    for(int i=0;i<mManagedClients.size();i++)//restack transients
    {
        CManagedClient *lClient=mManagedClients[i];

        if(!lClient) continue;
        if(lClient->IsTransient())
        {
            if(lClient->GetTransientFor()==(Window)GetID()) //root window transient
            {
                if(!lClient->GetGroupLeader()) continue;
                for(int j=0;j<mManagedClients.size();j++)
                {
                    if(i==j) continue;
                    CManagedClient *lClient2=mManagedClients[j];
                    if(lClient2->GetTransientFor()==(Window)GetID()) continue;
                    if(lClient2->GetGroupLeader()!=lClient->GetGroupLeader())
                        continue;

                    if(mClientsStack[j]->mStackingOrder>
                        mClientsStack[i]->mStackingOrder)
                    {
                        mClientsStack[i]->mStackingOrder=
                            mClientsStack[j]->mStackingOrder+1;
                    }
                }
            }
            else
            {
                for(int j=0;j<mManagedClients.size();j++)
                {
                    if(lClient->GetTransientFor()!=
                            (Window)mClientsStack[j]->mID)
                        continue;

                    if(mClientsStack[j]->mStackingOrder>
                        mClientsStack[i]->mStackingOrder)
                    {
                        mClientsStack[i]->mStackingOrder=
                            mClientsStack[j]->mStackingOrder+1;
                    }
                }
            }
        }
    }

    std::sort(mClientsStack.begin(),mClientsStack.end(),CClient::CompareByStack);

    int lCount=mClientsStack.size();
    Window lList[lCount*4];

    int lNum=0;

    for(int i=lCount-1;i>=0;i--)
    {
        if(CManagedClient *lC=dynamic_cast<CManagedClient*>(mClientsStack[i]))
        {
            if(lC->IsWithdrawn())
                continue;
            Window lID=lC->GetID();
            if(lID)
            {
                CDecoration *lD=lC->mDecoration;

                if(lD)
                {
                    for(int j=0;j<lD->mDecoWindows.size();j++)
                        lList[lNum++]=lD->mDecoWindows[j];
                }
                lList[lNum++]=lID;
            }
            lC->mStackingOrder=i*2+1; //1,3,5,7...
        }
    }

    XRestackWindows(gui::x11::get_default_x11_display()->GetDisplay(),lList,lNum);

    NETSetStackingList();

    OnRestack();
}

void CRoot::WMRaise(CManagedClient *pClient)
{
    //Bottom is last... our top window
    pClient->mStackingOrder=(1<<29);
    WMRestack();
}

void CRoot::WMLower(CManagedClient *pClient)
{
    //Bottom is last... our top window
    pClient->mStackingOrder=0;
    WMRestack();
}

///////////////////////////////////////////////////////////////////
//                                                               //
//                         event handling                        //
//                                                               //
///////////////////////////////////////////////////////////////////

void CRoot::OnKeyDown(const gui::CKeyEvent &pE)
{
    if(pE.mMods&KEYMOD_ALT)
    {
        if(pE.mKey==KEY_TAB)
        {
            if(mDesktopRenderer!=mShuffleRenderer)
            {
                if(!mShuffleRenderer)
                {
                    mShuffleRenderer=new CShuffleRenderer;
                    mShuffleRenderer->sOutFinished.Connect(&CRoot::OnOutFinished,this);
                }
                SetDesktopRenderer(mShuffleRenderer);

                XGrabKeyboard(gui::x11::get_default_x11_display()->GetDisplay(),GetID(),
                        true,GrabModeAsync,GrabModeAsync,
                              gui::x11::get_x11_toolkit()->GetTimeStamp());
            }
            else if(mGoingOut)
            {
                mShuffleRenderer->GoIn();

                XGrabKeyboard(gui::x11::get_default_x11_display()->GetDisplay(),GetID(),
                        true,GrabModeAsync,GrabModeAsync,
                              gui::x11::get_x11_toolkit()->GetTimeStamp());
            }

            if(pE.mMods&KEYMOD_SHIFT)
            {
                mShuffleRenderer->Backward();
            }
            else
            {
                mShuffleRenderer->Forward();
            }
        }
        else if(pE.mKey==KEY_F4)
        {
            if(mActiveClient&&mActiveClient->CanClose())
            {
                mActiveClient->Close();
            }
        }
        else if(pE.mKey==KEY_RIGHT)
        {
            if(pE.mMods&KEYMOD_CONTROL)
            {
                SwitchToDesktop((mDesktops.mActive+1)%mDesktops.mCount,true);
            }
        }
        else if(pE.mKey==KEY_LEFT)
        {
            if(pE.mMods&KEYMOD_CONTROL)
            {
                SwitchToDesktop((mDesktops.mActive-1)%mDesktops.mCount,true);
            }
        }
    }
    else if(pE.mMods&KEYMOD_WIN)
    {
        if(pE.mKey==KEY_S)
        {
            mShowStatistics=!mShowStatistics;
            DamageAll();
        }
        if(pE.mKey==KEY_D)
        {
            mUseDepthTest=!mUseDepthTest;
            DamageAll();
        }
    }
}

void CRoot::OnKeyUp(const gui::CKeyEvent &pE)
{
    if(pE.mKey==KEY_LALT)
    {
        if(mDesktopRenderer==mShuffleRenderer)
        {
            mShuffleRenderer->GoOut();
            XUngrabKeyboard(gui::x11::get_default_x11_display()->GetDisplay(),CurrentTime);
            mGoingOut=true;
            WMActivate(mShuffleRenderer->GetCurrentClient());
        }
    }
}

void CRoot::OnClientRemoved(CClient *pClient)
{
    erase_all_equal(gRoot->mClients,pClient);
    erase_all_equal(gRoot->mClientsStack,pClient);

    NETSetClientList();
    NETSetStackingList();
}

void CRoot::OnDesktopsChanged()
{
    mDesktops.mWholeViewport=CSize_i(GetSize().x*mDesktops.mCount,GetSize().y);
    mDesktops.mAngleCovered=360.0/(double)mDesktops.mCount;
    mDesktops.mDistance=1.0/(2.0*tan(mDesktops.mAngleCovered*M_PI/360.0));

    for(int i=0;i<mDesktops.mCount;i++)
    {
        mDesktops.mAngle[i]=mDesktops.mAngleCovered*(double)i;
        mDesktops.mPos[i]={int(GetSize().x*i),0};
    }

    for(int i=0;i<mClients.size();i++)
    {
        mClients[i]->OnDesktopsChanged();
    }

    NETSetDesktopInfo();
    NETSetDesktopNames();
}

void CRoot::OnExpose(const gui::CExposeEvent &pE)
{
}

void CRoot::OnOutFinished()
{
    if(mDesktopRenderer==mShuffleRenderer)
    {
        SetDesktopRenderer(mDefaultDesktopRenderer);
    }

    mGoingOut=false;
}

void CRoot::ShowOutputWindow()
{
    XserverRegion lRegion;
    lRegion=XFixesCreateRegion(gui::x11::get_default_x11_display()->GetDisplay(),nullptr,0);
    XFixesSetWindowShapeRegion(gui::x11::get_default_x11_display()->GetDisplay(),mOverlayWindow->GetX11ID(),
                            ShapeBounding,0,0,0);
                            XFixesSetWindowShapeRegion(gui::x11::get_default_x11_display()->GetDisplay(),mOverlayWindow->GetX11ID(),
                            ShapeInput,0,0,lRegion);
                            XFixesDestroyRegion(gui::x11::get_default_x11_display()->GetDisplay(),lRegion);

    DamageAll();
}

void CRoot::HideOutputWindow()
{
    XserverRegion lRegion;
    lRegion=XFixesCreateRegion(gui::x11::get_default_x11_display()->GetDisplay(),nullptr,0);
    XFixesSetWindowShapeRegion(gui::x11::get_default_x11_display()->GetDisplay(),mOverlayWindow->GetX11ID(),
                            ShapeBounding,0,0,lRegion);
                            XFixesDestroyRegion(gui::x11::get_default_x11_display()->GetDisplay(),lRegion);
}

void CRoot::InitGLX()
{
    gui::x11::CVisual lVisual(gui::x11::get_default_x11_display()->GetVisual());

    XVisualInfo *lVisualInfo=lVisual.GetVisualInfo();
    mDefaultDepth=lVisualInfo->depth;

    mOverlayWindow=new gui::x11::CX11Window(XCompositeGetOverlayWindow(gui::x11::get_default_x11_display()->GetDisplay(),GetID()),
                                            gui::x11::get_default_x11_display());
    ShowOutputWindow();

    mGLContext=new gui::x11::CGLXContext(mOverlayWindow,true);
    mGLContext->MakeCurrent(mOverlayWindow);
}

void CRoot::InitGL()
{
    glClearColor(0.0,0.0,0.0,1.0);
    glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_CULL_FACE);
    glDisable(GL_BLEND);
    glDisable(GL_CULL_FACE);

///\todo     img::CImage_gray8 lShadow=img::gaussian_map(10.0,0.5,{200,200});
    img::CImage_rgba8 lBlack({1,1},img::pixel(0,0,0,1));
    mShadowTexture.Create2D(lBlack);
}

void CRoot::InitComposite()
{
    gui::x11::CComposite::RedirectSubwindows(GetID());

    get_main_event_loop().AddFunction(CEventLoop::PRIORITY_NORMAL,this);

    InitGLX();
    InitGL();

    mCubeRenderer=new CCubeRenderer;
    mDefaultDesktopRenderer=new CDefaultDesktopRenderer;

    SetSceneRenderer(mCubeRenderer);
    SetDesktopRenderer(mDefaultDesktopRenderer);

    mRegions.mDesktop=CRect_d(CPoint_d(0,0),GetSize());
    //TODO:detectRefreshRate
}

void CRoot::SetSceneRenderer(CSceneRenderer *pRenderer)
{
    if(mSceneRenderer==pRenderer) return;

    if(mSceneRenderer)
        mSceneRenderer->Disable();
    mSceneRenderer=pRenderer;
    mSceneRenderer->Reshape(mOverlayWindow->GetX11ID(),GetSize());
    mSceneRenderer->Enable();
}

void CRoot::SetDesktopRenderer(CDesktopRenderer *pRenderer)
{
    if(mDesktopRenderer==pRenderer) return;

    if(mDesktopRenderer)
        mDesktopRenderer->Disable();
    mDesktopRenderer=pRenderer;
    mDesktopRenderer->Enable();
}

void CRoot::WaitForVSync()
{
    unsigned int lSync;
    glXGetVideoSyncSGI(&lSync);
    glXWaitVideoSyncSGI(2,(lSync+1)%2,&lSync);
}

void CRoot::OnComposeTimer()
{
}

bool CRoot::OnLoopFunction()
{
    mGLContext->MakeCurrent(mOverlayWindow);

    mTime.mPrevious=mTime.mCurrent;
    mTime.mCurrent=CMonotonicClock::now();
    mTime.mElapsed=mTime.mCurrent-mTime.mPrevious;

    mFPS.Frame(mTime.mElapsed);

    for(int i=0;i<mClients.size();i++)
    {
        mClients[i]->OnLoop(mTime.mCurrent);
    }

    mDesktopRenderer->OnLoop();
    mSceneRenderer->OnLoop();

    mSceneRenderer->Render();
    
    auto lInterval=CMonotonicClock::now()-mTime.mCurrent;

    //msg_info() << "Interval " << lInterval << "\n";

    if(lInterval<mRedrawTime)
        lInterval=mRedrawTime;
    else if(lInterval>mRedrawTime*5)
        lInterval=mRedrawTime*5;

    //msg_info() << "Real interval " << lInterval << "\n";

//  lInterval=8;

//  lInterval=17;
    lInterval=chrono::milliseconds(1);
    mTimer->SetInterval(lInterval);
    end_timer();
    msg_info() << "ending "; abs_timer();
    start_timer();

    return true;
}

void CRoot::OnBackgroundPropChanged()
{
    DamageAll();
}

void CRoot::OnRootExpose(XExposeEvent *pE)
{
/*  CRect_d lRect(CPoint(pE->x,pE->y),CSize(pE->width,pE->height));

    mRegions.mRootExpose.Add(lRect);

    if(pE->count==0)
    {
        DamageViewport(mRegions.mRootExpose);
        mRegions.mRootExpose.Clear();
    }
*/
}

void CRoot::OnRootConfigure()
{
    DamageAll();
}

void CRoot::OnRestack()
{
    DamageAll();
}

int CRoot::AcquireClientDataSlot(void *pData)
{
    if(mClientData.mNumSlots<mClientData.mSlots.size())
    {
        for(int i=0;i<mClientData.mSlots.size();i++)
        {
            if(!mClientData.mSlots[i])
            {
                mClientData.mSlots[i]=pData;
                mClientData.mNumSlots++;
                return i;
            }
        }
    }
    else
    {
        mClientData.mSlots.push_back(pData);
        return mClientData.mNumSlots++;
    }

    throw XInternal("can't acquire data slot... should never happen");
}

void *CRoot::ReleaseClientDataSlot(int pSlot)
{
    assert(pSlot<mClientData.mSlots.size());
    void *lData=mClientData.mSlots[pSlot];
    mClientData.mSlots[pSlot]=nullptr;
    mClientData.mNumSlots--;

    return lData;
}

/*namespace bwm*/ } /*namespace buola*/ }
